module Gamifier
  module DSL
    class Site
      attr_reader :behaviors
      attr_reader :units
      attr_reader :rewards
      attr_reader :missions
      attr_reader :tracks
      attr_reader :source

      def initialize(*args)
        @source = ::Gamifier::Site.new(*args)
        @behaviors = []
        @units = []
        @rewards = []
        @missions = []
        @tracks = []
      end

      def engine
        source.engine
      end

      def behavior(name, &block)
        new_behavior = engine.activity_definitions.build(:name => name)
        behaviors.push new_behavior

        DSL.eval_with_context(new_behavior, &block)
      end

      def reward(name, &block)
        new_reward = engine.reward_definitions.build(:name => name)
        rewards.push new_reward

        DSL.eval_with_context(new_reward, &block)
      end

      def unit(name, &block)
        new_unit = engine.units.build(:name => name)
        units.push new_unit

        DSL.eval_with_context(new_unit, &block)
      end

      def mission(name, &block)
        new_mission = engine.groups.build(:name => name)
        missions.push new_mission

        DSL.eval_with_context(new_mission, &block)
      end

      def track(name, &block)
        new_track = engine.tracks.build(:label => name)
        tracks.push new_track

        DSL.eval_with_context(new_track, &block)
      end

      def save
        if super
          save_units!
          save_behaviors!
          save_rewards!
          save_missions!
          save_tracks!
        else
          raise Error, "Can't save #{self}"
        end
      end

      def save_units!
        units.each do |unit|
          Gamifier.logger.info "Saving units..."
          unit.site = self.url
          if gunit = engine.send(unit.class.path).find_by_name(unit.name, :site => unit.site)
            unit._id = gunit._id
            # FIXME: once Badgeville returns the unit _id in the
            # response, remove the next line:
            next
          end
          unit.engine = engine
          unit.save || raise(Error, "Can't save #{unit}")
        end
      end

      def save_behaviors!
        behaviors.each do |behavior|
          Gamifier.logger.info "Saving behaviors..."
          behavior.site_id = self._id
          if gbehavior = engine.send(behavior.class.path).find_by_name(behavior.name, :site => self.url)
            behavior._id = gbehavior._id
          end
          behavior.engine = engine
          behavior.save || raise(Error, "Can't save #{behavior}")
        end
      end

      def save_rewards!
        rewards.each do |reward|
          Gamifier.logger.info "Saving rewards..."
          reward.site_id = self._id
          if greward = engine.send(reward.class.path).find_by_name(reward.name, :site => self.url)
            reward._id = greward._id
          end
          reward.engine = engine
          reward.save || raise(Error, "Can't save #{reward}")
        end
      end

      def save_missions!
        missions.each do |mission|
          Gamifier.logger.info "Saving missions..."
          mission.site_id = self._id
          if gmission = engine.send(mission.class.path).find_by_name(mission.name, :site => self.url)
            mission._id = gmission._id
          end
          mission.engine = engine
          mission.save || raise(Error, "Can't save #{mission}")
        end
      end

      def save_tracks!
        tracks.each do |track|
          Gamifier.logger.info "Saving tracks..."
          track.site_id = self._id
          if gtrack = engine.send(track.class.path).find_by_label(track.label, :site => self.url)
            track._id = gtrack._id
          end
          track.engine = engine
          track.save || raise(Error, "Can't save #{track}")
        end
      end

      def method_missing(*args, &block)
        source.send(*args, &block)
      end
    end
  end
end
